package agg

import (
	"gitee.com/yysf_xin/dplog/pkg/config"
	"gitee.com/yysf_xin/dplog/pkg/push"
	"gitee.com/yysf_xin/dplog/pkg/slog"
	"gitee.com/yysf_xin/go-common/db"
	"gitee.com/yysf_xin/go-common/tools"
	"github.com/bluele/gcache"
	"github.com/dexyk/stringosim"
	"time"
)

type LogAggregation struct {
	Cache    gcache.Cache
	Notifier push.MsgNotifier
	cfg      *config.Agg
}

func New(cfg *config.Agg, n push.MsgNotifier) *LogAggregation {
	if cfg.Span > 0 {
		n = push.NewTimeDuration(time.Duration(cfg.Span), n)
	}
	l := &LogAggregation{
		cfg:      cfg,
		Notifier: n,
	}
	gb := gcache.New(cfg.Size).ARC().EvictedFunc(func(key, value interface{}) {
		values := *value.(*push.LoggerGroup)
		for _, v := range values {
			if v.Count > 1 && v.First != v.Last {
				l.Notifier.DoNotify(v)
			}
			v.Ref.Release()
		}
	})
	if cfg.Expire > 0 {
		gb.Expiration(time.Duration(cfg.Expire))
	}
	l.Cache = gb.Build()
	return l
}

func (l *LogAggregation) FireMsg(ref *slog.MessageRef) {
	msg := ref.GetRef()
	var lv int
	{
		levelStr := msg.GetLevel()
		if levelStr == "" {
			return
		}
		level := levelStr[0]

		for i, v := range slog.LEVEL {
			if level == v {
				lv = i
				break
			}
		}
	}
	if lv < l.cfg.Level {
		return
	}
	key := msg.GetLogger()
	{
		m := msg.GetMethod()
		if m != "" {
			key += "." + m
		}
	}

	v, _ := l.Cache.Get(key)

	var sample *push.MsgRef
	if v == nil {
		ref.Retain()
		src := []rune(tools.BytesToString(msg.GetMessage()))
		CleanRune(src)
		counters := make(push.LoggerGroup, 1)
		sample = &push.MsgRef{
			Msg: push.Msg{
				Msg:   *msg.(*slog.MessageImpl),
				Meta:  ref.Meta,
				First: db.Time(*msg.GetTime()),
				Count: 1,
			},
			Ref: ref,
			Str: src,
		}
		counters[0] = sample
		_ = l.Cache.Set(key, &counters)
	} else {
		group := v.(*push.LoggerGroup)
		var (
			dfV float64
		)
		str := []rune(tools.BytesToString(msg.GetMessage()))
		CleanRune(str)
		for _, c := range *group {
			vl := stringosim.JaroWinkler(str, c.Str)
			if dfV < vl {
				dfV = vl
			}
			if vl > l.cfg.Threshold {
				sample = c
				break
			}
		}
		if sample != nil {
			sample.Count++
		} else {
			ref.Retain()
			if ref.Count == 0 {
				ref.Count++
			}
			sample = &push.MsgRef{
				Msg: push.Msg{
					Msg:   *msg.(*slog.MessageImpl),
					Meta:  ref.Meta,
					First: db.Time(*msg.GetTime()),
					Count: ref.Count,
				},
				Ref: ref,
				Str: str,
			}
			*group = append(*group, sample)
		}
	}
	sample.Last = db.Time(*msg.GetTime())
	l.Notifier.DoNotify(sample)

}
